import { setItem, getItem } from '@/utils/storage'

export default {
  namespaced: true, // 如果不加，默认值为 false，那么所有的 mutations/actions/getters 都会挂载到根模块上
  state: {
    // 如果没有登陆过，去本地拿的时候，拿不到，所以要给个初始空对象，为了使 changeUserToken mutation 中的 Object.assign 能够正常执行，否则会：Object.assign(null, {})
    userToken: getItem('user_token') || {},
  },
  getters: {
    // 是否登录了
    isLogin(state) {
      return !!(state.userToken && state.userToken.token)
    },
  },
  mutations: {
    changeUserToken(state, payload) {
      if (payload === null || payload === undefined) {
        state.userToken = {}
        localStorage.removeItem('user_token')
      } else {
        // Object.assign(obj1, obj2, obj3, ...) 这个方法可以用来做浅拷贝
        // 1. 从第二个对象开始，逐个将键值对合并添加到第一个对象中，如果第一个对象中已经存在，则覆盖
        // 2. 最终会返回第一个对象
        state.userToken = Object.assign({}, state.userToken, payload)
        setItem('user_token', state.userToken)
      }
    },
  },
  actions: {
    // 页面上通过 this.$store.dispatch 调用 action 时，返回值为 promise 实例
    // this.$store.dispatch('user/login', { mobile, code }).then(() => { console.log('页面组件中调用成功后的逻辑') })
    login(ctx, payload) {
      // this 既然是 Vuex.Store 的实例对象，那么 也可以把 api 挂到 Vuex.Store.prototype 上
      // return this._apis.user.login(payload).then(res => {
      //   ctx.commit('changeUserToken', res.data.data)
      // })
      const p1 = this._apis.user.login(payload)
      const p2 = p1.then(res => {
        ctx.commit('changeUserToken', res.data.data)
        return res
      })
      // const p3 = p2.then(res => {
      //   // 这里的 res 就是上一次 .then 函数的成功函数的返回值
      // })
      return p2
    },
  },
}

// 假设这个 dispatch 函数就是 store 实例的 dispatch 函数
function dispatch(actionName, payload) {
  return new Promise((resolve, reject) => {
    // 这个是 new Promise 实例时的执行器函数，是同步执行的

    // 内部可以根据 actionName 找到 actionFn
    const actionFn = findActionFnBy(actionName)
    // actionFnReturnValue 就是上面 login action 返回的 p2
    const actionFnReturnValue = actionFn({ commit, dispatch }, payload)

    // 这个 dispatch 函数返回的 promise 实例的状态什么时候改变呢？
    // 要等 actionFn 调用完后，看 actionFn 的返回值类型是不是又是一个 promise 实例
    // -- 如果是，那么 dispatch 函数返回的 promise 实例 就受 actionFn 返回的 promise 实例 的影响
    // -- 如果不是，那么就立即调用 resolve 函数将 dispatch 返回的 promise 实例的状态改成 fulfilled，并且将 actionFn 的返回值 作为 fulfilled 状态的 value

    if (actionFnReturnValue instanceof Promise) {
      actionFnReturnValue.then(
        value => {
          // 进到这里，说明 action 返回的 promise 实例的状态 终于变成 fulfilled 了
          // 那么 dispatch 返回的 promise 实例 就可以也变成 fulfilled 了，并且把 fulfilled 的 value 传递下去
          resolve(value)
        },
        reason => {
          // 进到这里，说明 action 返回的 promise 实例的状态 变成 rejected 了
          // 那么 dispatch 返回的 promise 实例 也应该变成 rejected，并且要把 错误的原因 reason 继续向下抛，让后面的业务逻辑代码去处理异常
          reject(reason)
        }
      )
    } else {
      resolve(actionFnReturnValue)
    }
  })
}
